/* $Header: p_tcomp.c,v 2.2 01/10/02 10:27:53 przybyls Exp $ */

/***********************************************************************/
/*                                                                     */
/*                  Copyright Hewlett-Packard Co. 1991                 */
/*                                                                     */
/***********************************************************************/

/***********************************************************************/
/* MODULE NAME: tcomp                                                  */
/* ============                                                        */
/*                                                                     */
/* Author: wikfors                                                     */
/*                                                                     */
/* MODULE PURPOSE:  apply sensor calibrations to setpoints before      */
/*  transfer to the pcb and to actuals before they are used.           */
/*  With this scheme, all tables represent ideal conditions, and       */
/*  any unique sensor behavior is isolated here.                       */
/*                                                                     */
/***********************************************************************/


/*****************************/
/*   GLOBAL INCLUDE FILES    */
/*****************************/

#include <typedefs.h>
#include <error.h>
#include <p_wkfile.h>
#include <p_status.h>
#include <math.h>
#include <list_mgmt.h>
#include <pcb_defs.h>
#include <keyboard.h>

/*****************************/
/*   LOCAL INCLUDE FILES     */
/*****************************/

#include "proto.h"
#include "p_tcomp.h"

/*****************************/
/*   GLOBAL VARIABLES        */
/*****************************/


/*****************************/
/*   EXTERNAL VARIABLES      */
/*****************************/


/*****************************/
/*   LOCAL DEFINES           */
/*****************************/

#define MIN_TCD_REF_FLOW  5     /* 5.0 ml/min default TCD ref flow if not in eeprom */
#define MIN_LDID_GAS_FLOW 5     /* 5.0 ml/min default LDID gas flow if not in eeprom */

/*****************************/
/*   STATIC VARIABLES        */
/*****************************/


/*****************************/
/*   IMPLEMENT SECTION       */
/*****************************/
/**begin_proto**/

#pragma SECTION PROG=rom3

/**end_proto**/


U_INT16 ConvertTempToIu ( float temp )
{
     return (U_INT16)( temp * 64.0 );
}


/**begin_proto**/

U_INT16 FindTrueTemp ( pTHERMISTOR_PARMS parms, U_INT16 volts )

/**end_proto**/
{

#if 1
              /* 25C          delta V             dV/dT */

       return  19081 + (INT32)( volts - parms->V_25C ) * parms->dT / parms->dV;

#else

  float log_part, temp_part;

     /* correct for non linearities in   */
     /* the thermister.                  */


     /* since:                           */
     /*              b( 1/t1 - 1/t0 )    */
     /*    v1 = v0 e                     */
     /*                                  */
     /*               1                  */
     /*        ----------------------    */
     /*  t1 =       v1       1           */
     /*         ln( -- )  + ---          */
     /*             v0       t0          */
     /*         --------                 */
     /*             b                    */



     log_part = log((float)volts/(float)parms->v0)/(float)parms->beta;

     temp_part = 1/(float)ConvertTempToIu(298.15);

     return  1.0/( log_part + temp_part );

#endif

}


/**begin_proto**/

BIT8 GetThermisterConstants ( U_INT8 module_number, pTHERMISTOR_PARMS parms )

/**end_proto**/
{
     BIT8 result;

     result = TRUE;

     result &= ReadEeprom( module_number, THERMISTOR_V_25C,       &parms->V_25C );
     result &= ReadEeprom( module_number, THERMISTOR_dV,          (U_INT16 *)&parms->dV    );
     result &= ReadEeprom( module_number, THERMISTOR_dT,          (U_INT16 *)&parms->dT    );
     return result;

}

/**begin_proto**/

void BuildThermistorTable ( U_INT8 module_number, pTHERMISTOR_STRUCT thermistor_table )

/**end_proto**/
{
     U_INT16 i;
     if ( !GetThermisterConstants ( module_number, &thermistor_table->Parms  ) )
     {
          TrashModule( module_number );
          return;
     }

     for ( i = 0 ; i < 256 ; i++ )
     {
         *( thermistor_table->Volts_To_Temp + i) = FindTrueTemp( &thermistor_table->Parms, i*256 );
     }

     *( thermistor_table->Volts_To_Temp + i) = FindTrueTemp( &thermistor_table->Parms, 0xffff );
}


/**begin_proto**/

void BuildDefaultThermistorTable ( pTHERMISTOR_STRUCT thermistor_table )

/**end_proto**/
{
     U_INT16 i;

      /* the only reason for building a default temp table is so that
         manufacturing inlet calibration can be temperature independant.   */

      /* these defaults were taken from my ss manifold.  there is no magic
         to their values, just that they must be consistent with defaults
         that are stored in the eeprom by TPxxZD                           */


     thermistor_table->Parms.V_25C = DEFAULT_V25C;
     thermistor_table->Parms.dV    = DEFAULT_DV;
     thermistor_table->Parms.dT    = DEFAULT_DT;

     for ( i = 0 ; i < 256 ; i++ )
     {
         *( thermistor_table->Volts_To_Temp + i) = FindTrueTemp( &thermistor_table->Parms, i*256 );
     }

     *( thermistor_table->Volts_To_Temp + i) = FindTrueTemp( &thermistor_table->Parms, 0xffff );
}

/***********************************************************************/
/* FUNCTION:                                                           */
/* ========                                                            */
/*                                                                     */
/* PURPOSE:                                                            */
/*                                                                     */
/* RETURNED VALUE:                                                     */
/*                                                                     */
/* GLOBALS USED:                                                       */
/*                                                                     */
/***********************************************************************/

U_INT16 PowerOfTwo( U_INT32 num )
{

     U_INT16 power;

     if ( num <= 1 ) return 0;

     for ( power = 1 ; ( (U_INT32)1 << power) < num ; power++ ) {};

     return power;
}


/**begin_proto**/

void FindFlowMasks ( U_INT32 max_flow, U_INT16 table_size,
                     U_INT32 *mask, U_INT32 *entry_spacing, U_INT16 *shift_value, U_INT32 IU_Scaling )

/**end_proto**/
{

  /* first see if the max flow is less than the size of the table.  */
  /* if it is, we can space the table with 1 mL/min spacing.        */
  /* if it is not, we want to keep doubling the spacing until the   */
  /* table will fit into the table size.                            */

     *entry_spacing = IU_Scaling;
     *shift_value   = PowerOfTwo( IU_Scaling ) - 8;
     *mask          = 0x0000ffff << *shift_value;

     while (( max_flow / *entry_spacing ) > table_size )
     {
          *entry_spacing = *entry_spacing << 1;
          *shift_value   = *shift_value    + 1;
          *mask          = 0x0000ffff << *shift_value;
     }
}

/***********************************************************************/
/* FUNCTION:                                                           */
/* ========                                                            */
/*                                                                     */
/* PURPOSE:                                                            */
/*                                                                     */
/* RETURNED VALUE:                                                     */
/*                                                                     */
/* GLOBALS USED:                                                       */
/*                                                                     */
/***********************************************************************/


/**begin_proto**/

void GetPresSensorConstants ( U_INT8 module_number, U_INT8 gas_num, pPRES_SENSOR_PARMS parms )

/**end_proto**/
{
     BIT8 result;

     result = TRUE;

     result &= ReadEeprom( module_number, PS_RANGE+gas_num-1,                 &parms->Range );
     result &= ReadEeprom( module_number, PS_OFFSET+gas_num-1,     (U_INT16 *)&parms->Offset );
     result &= ReadEeprom( module_number, PS_DOFFSET_DT+gas_num-1, (U_INT16 *)&parms->dOffset_dT );
     result &= ReadEeprom( module_number, PS_SPAN+gas_num-1,                  &parms->Span );
     result &= ReadEeprom( module_number, PS_DSPAN_DT+gas_num-1,   (U_INT16 *)&parms->dSpan_dT );

     if ( !result )
     {
        TrashModule( module_number );
     }
}


void GetFritConstants( pDETECTOR_STATUS status, U_INT8 gas_num, pFRIT_PARMS frit )
{

   U_INT8   gas_type;
   U_INT32  iu_scaling;
   float    alpha_conv;
   float    beta_conv;
   BIT8     result;

   union { struct { U_INT16 word1; U_INT16 word2;} Short;
           U_INT32                                 Long;
         } num;

   result = TRUE;

   switch ( gas_num )
   {
       case 1: gas_type   = (U_INT8)status->Config->Gas_1.Gas_Type;
               iu_scaling = status->Gas_1.IU_Scaling;
               break;
       case 2: gas_type   = (U_INT8)status->Config->Gas_2.Gas_Type;
               iu_scaling = status->Gas_2.IU_Scaling;
               break;
       default:
       case 3: gas_type   = (U_INT8)status->Config->Gas_3.Gas_Type;
               iu_scaling = status->Gas_3.IU_Scaling;
               break;
   }

   result &= ReadEeprom( status->Module_Number, Frit_Parm_Address_Table[ gas_type ] [ 0 ] [ gas_num-1 ], &num.Short.word1 );
   result &= ReadEeprom( status->Module_Number, Frit_Parm_Address_Table[ gas_type ] [ 1 ] [ gas_num-1 ], &num.Short.word2 );
   frit->frit_1 = num.Long;

   result &= ReadEeprom( status->Module_Number, Frit_Parm_Address_Table[ gas_type ] [ 2 ] [ gas_num-1 ], &num.Short.word1 );
   result &= ReadEeprom( status->Module_Number, Frit_Parm_Address_Table[ gas_type ] [ 3 ] [ gas_num-1 ], &num.Short.word2 );
   frit->frit_2 = num.Long;

   num.Short.word1 = 0;
   if (Frit_Parm_Address_Table[ gas_type ] [ 4 ] [ gas_num-1 ] != 0 )
   {
        result &= ReadEeprom( status->Module_Number,
                   Frit_Parm_Address_Table[ gas_type ] [ 4 ] [ gas_num-1 ], &num.Short.word2 );
   }
   else
   {
   num.Short.word2 = 0;
   }

   if ( ! result )
   {
       TrashModule ( status->Module_Number );
       return;
   }

   frit->Max_Flow = num.Long * iu_scaling;

   if (frit->Max_Flow != 0 )
   {
        GetFritConversionConst ( status->Module_Number, gas_num, (GAS_TYPE)gas_type, &alpha_conv, &beta_conv );

        frit->Alpha = (float)frit->frit_1 * alpha_conv;
        frit->Beta  = (float)frit->frit_2 * beta_conv;
   }

}

/**begin_proto**/

void ReadDetMaxFlowRates ( pDETECTOR_STATUS status, U_INT8 gas_num )

/**end_proto**/
{

   pFRIT_PARMS frit_parms;
   U_INT32     iu_scaling;
   U_INT16     max_flow;
   U_INT8      gas_type;

   switch ( gas_num )
   {
       case 1: frit_parms = &status->Gas_1.Tcomp_Tables->Frit.Parms;
               iu_scaling = status->Gas_1.IU_Scaling;
               break;
       case 2: frit_parms = &status->Gas_2.Tcomp_Tables->Frit.Parms;
               iu_scaling = status->Gas_2.IU_Scaling;
               break;
       default:
       case 3: frit_parms = &status->Gas_3.Tcomp_Tables->Frit.Parms;
               iu_scaling = status->Gas_3.IU_Scaling;
               break;
   }

   for ( gas_type = (U_INT8)N2 ; gas_type < (U_INT8)UNKNOWN_GAS ; gas_type++ )
   {
        if ( ValidateDetectorGasType( status, gas_num, (GAS_TYPE)gas_type ) == OK )
        {
            if ( !ReadEeprom( status->Module_Number, Frit_Parm_Address_Table[ gas_type ] [ 4 ] [ gas_num-1 ], &max_flow ) )
            {
                 TrashModule( status->Module_Number );
            }
        }
        else
        {
            max_flow = 0;
        }

        frit_parms->Max_Gas_Flow_Rates [ gas_type ] = (U_INT32)max_flow * iu_scaling;
    }
}

/**begin_proto**/

void ReadDetGasMaxFlowRates ( pDETECTOR_STATUS status )

/**end_proto**/
{
      ReadDetMaxFlowRates ( status, 1 );
      ReadDetMaxFlowRates ( status, 2 );
      ReadDetMaxFlowRates ( status, 3 );
}

/**begin_proto**/

void GetFritConversionConst ( U_INT8 module, U_INT8 gas_num, GAS_TYPE gas_type, float *alpha_conv, float *beta_conv )

/**end_proto**/
{
   U_INT32  iu_scaling;
   U_INT16  dcm2_per_iu;

   ReadPresScaling( module, gas_num, &dcm2_per_iu );
   ReadFlowScaling( module, gas_num, &iu_scaling );

   *alpha_conv = pow( 68947.57/dcm2_per_iu, 2 ) /                       /* convert psi to pres iu     */
                   ( iu_scaling *                                       /* convert mL/min to flow iu  */
                     (298.15/273.15) *                                  /* convert sccm to mL/min NTP */
                     pow( 64, 1.0+GetViscosityExponent(gas_type) ));    /* convert oK to temp iu      */

   *beta_conv  = (68947.57/dcm2_per_iu) /                               /* convert psi to pres iu     */
                   ( iu_scaling *                                       /* convert mL/min to flow iu  */
                     (298.15/273.15) *                                  /* convert sccm to mL/min NTP */
                     pow( 64, 0.5 ));                                   /* convert oK to temp iu      */

}


void GetConfigConstants( pDETECTOR_STATUS status, pDET_GAS_TABLE gas_table )
{

    U_INT16 num_entries;
    U_INT32 step_rate;
    U_INT16 shift;
    U_INT32 mask;
    U_INT32 max_flow;

    /* find maximum flow in mL/min  (IU) */

    max_flow = gas_table->Frit.Parms.Max_Flow;

    /* create a mask that will take the 8 most significant bits of the     */
    /* flow number in the upper byte, and use the lower byte for the next  */
    /* most signifgicant digits                                            */

    FindFlowMasks ( max_flow, REF_TABLE_SIZE, &mask, &step_rate, &shift, status->IU_Scaling );


    /* the table resolution only needs to be one entry per mL/min, so if  */
    /* max flow < Max Table Size, make the table max flow entries long    */

    num_entries = ( (gas_table->Frit.Parms.Max_Flow/step_rate) < REF_TABLE_SIZE )
                  ? (gas_table->Frit.Parms.Max_Flow/step_rate) +1
                  :  REF_TABLE_SIZE;

    gas_table->Frit.Mask        = mask;
    gas_table->Frit.Shift_Value = shift;
    gas_table->Frit.Step_Rate   = step_rate;
    gas_table->Frit.Num_Entries = num_entries;

}


void GetTempParms( GAS_TYPE gas_type, float temp, float *T_5, float *T17 )
{

    *T_5 = pow( temp, 0.5 );
    *T17 = pow( temp, 1.0 + GetViscosityExponent(gas_type) );

}



U_INT16 ConvertDetFlowToPres ( pDET_GAS_TABLE gas_table, float T_5, float T17, float Po, float flow )
{

#if 0

Given (1) & (2), then (3) follows ( note the (1) comes from ConvertDetPresToFlow below ... )
============================================================================================

          f1                      f2
    F = ------ * [Pi2 - Po2]  +  ------ * Pi                            (1)
         T17                      T_5

         f1             f2                      2              2
    a = ----- ;    b = ---- ;    Pi2 = [Pi + Po]   ;   Po2 = Po         (2)
         T17            T_5


              2            2     2
    F = a*[ Pi + 2PiPo + Po  - Po  ] + b*Pi

            2
      = a*Pi + [2aPo + b ]*Pi  ;                                        (3)


Assume (4) and prove by showing it equal to (3) :
=================================================

                   1                2          .5
If   Pi = -Po  + ------ * [ -b + ( b  + 4*a*c )   ]                     (4)
                  2*a

                             f1              f2
         where    c = F  +  ----- * Po2  +  ----- * Po
                             T17             T_5

Then
        _               _ 2        2
       |            b    |        b      4*a*c
       | Pi + Po + ----  |   =  ----- + -------
       |_          2*a  _|      4*a^2    4*a^2


                                               2           2
                  2     b                     b           b        c
       [ Pi + Po ]  +  --- * [ Pi + Po ] +  -----   =   -----  +  ---
                        a                   4*a^2       4*a^2      a

             2              2
       a*[ Pi + 2*Pi*Po + Po ] + b*[ Pi + Po ]  =  c

           2                           2                   2
       a*Pi + [ 2*Pi*Po + b ]*Pi + a*Po + b*Po  =  F + a*Po  + b*Po

           2
       a*Pi + [ 2*Pi*Po + b ]*Pi   =  F                                 (5)



Since (5) = (3) , (4) is the correct equation for Pi = g(F)

  given F = G(Pi) as in (1) .


#endif

        float f1, f2, Pi;
        float a,b,c;
        float Po2;
        U_INT16 pres;

        f1 = gas_table->Frit.Parms.frit_1;
        f2 = gas_table->Frit.Parms.frit_2;

        Po2 = Po * Po;

        a = f1 / T17;

        b = f2 / T_5;

        c =  flow + f1*Po2/T17 + f2*Po/T_5;

        Pi = ( -b + sqrt( b*b + 4 * a * c ) ) / ( 2 * a ) - Po;

        /* we may have read unachievable flows from eeprom, cap them here. */

        if      ( Pi > 0xffff ) pres = 0xffff;
        else if ( Pi < 0 )      pres = 0;
        else                    pres = (U_INT16)Pi;

        return  pres;

}



U_INT32 ConvertDetPresToFlow( pDET_GAS_TABLE gas_table, float T_5, float T17, float Po, float pres )
{
        float Pi, f1, f2, flow;
        float Po2, Pi2;


        f1 = gas_table->Frit.Parms.frit_1;
        f2 = gas_table->Frit.Parms.frit_2;

        Pi = pres;

        Po2 = Po * Po;
        Pi2 = (Pi+Po) * (Pi+Po);


        flow =   f1 * (Pi2 - Po2) / T17  +  f2 * Pi / T_5;

        flow = ( flow > 0x80000001 ) ? 0 : flow;

        return  (U_INT32)flow;
}


void  BuildFlowToPresTable ( pDET_GAS_TABLE gas_table, GAS_TYPE gas_type )
{
    float   T_5, T17;
    U_INT16 index;
    float   flow;

    GetTempParms( gas_type, DET_TCOMP_STD_TEMP_IU, &T_5, &T17 );

    for ( index = 0 ; index < gas_table->Frit.Num_Entries ; index++ )
    {
         flow = (float)index * (float)gas_table->Frit.Step_Rate;
         *(gas_table->Frit.Flow_To_Pres + index) =
                   ConvertDetFlowToPres( gas_table, T_5, T17, (float)DET_TCOMP_STD_PRES_IU, (float)flow );
    }
}


void  BuildPresToFlowTable ( pDET_GAS_TABLE gas_table, GAS_TYPE gas_type )
{
    float T_5, T17;
    U_INT16 index;
    U_INT16 pres;

    GetTempParms( gas_type, DET_TCOMP_STD_TEMP_IU, &T_5, &T17 );

    for ( index = 0 ; index < REF_TABLE_SIZE-1 ; index++ )
    {
         pres = index * 0x0100;
         *(gas_table->Frit.Pres_To_Flow  + index) =
                   ConvertDetPresToFlow( gas_table, T_5, T17, DET_TCOMP_STD_PRES_IU, pres );
    }
}


void BuildInitialCorrectionTable ( pDET_GAS_TABLE gas_table, GAS_TYPE gas_type )
{

     U_INT8 entry_num;
     INT32 std_pres;
     INT32 cor_pres;
     INT32 std_flow;
     float T_5_std, T17_std, T_5_cor, T17_cor;

    GetTempParms( gas_type, DET_TCOMP_STD_TEMP_IU, &T_5_std, &T17_std );
    GetTempParms( gas_type, DET_TCOMP_COR_TEMP_IU, &T_5_cor, &T17_cor );


     entry_num = 0;
     std_pres = 0x0100;

     std_flow = ConvertDetPresToFlow( gas_table,
                                      T_5_std, T17_std,
                                      DET_TCOMP_STD_PRES_IU,
                                      std_pres );




     cor_pres = ConvertDetFlowToPres( gas_table,
                                      T_5_cor, T17_cor,
                                      DET_TCOMP_STD_PRES_IU,
                                      std_flow );

     gas_table->Frit.Setpt_Temp_Correct_Per_T_IU[entry_num] =
              (cor_pres - std_pres) * CORRECT_SCALE / (DET_TCOMP_COR_TEMP_IU - DET_TCOMP_STD_TEMP_IU);

     cor_pres = ConvertDetFlowToPres( gas_table,
                                      T_5_std, T17_std,
                                      DET_TCOMP_COR_PRES_IU,
                                      std_flow ) + (DET_TCOMP_COR_PRES_IU - DET_TCOMP_STD_PRES_IU);

     gas_table->Frit.Setpt_Pres_Correct_Per_P_IU[entry_num] =
              (cor_pres - std_pres) * CORRECT_SCALE / (DET_TCOMP_COR_PRES_IU - DET_TCOMP_STD_PRES_IU);


     std_pres = 0;

     for ( entry_num = 1 ; entry_num < CORRECT_TABLE_SIZE  ; entry_num++ )
     {
         std_pres += 0x800;
         std_flow = ConvertDetPresToFlow( gas_table,
                                          T_5_std, T17_std,
                                          DET_TCOMP_STD_PRES_IU,
                                          std_pres );

         cor_pres = ConvertDetFlowToPres( gas_table,
                                          T_5_cor, T17_cor,
                                          DET_TCOMP_STD_PRES_IU,
                                          std_flow );

         gas_table->Frit.Setpt_Temp_Correct_Per_T_IU[entry_num] =
                  (cor_pres - std_pres) * CORRECT_SCALE / (DET_TCOMP_COR_TEMP_IU - DET_TCOMP_STD_TEMP_IU);

         cor_pres = ConvertDetFlowToPres( gas_table,
                                          T_5_std, T17_std,
                                          DET_TCOMP_COR_PRES_IU,
                                          std_flow ) + (DET_TCOMP_COR_PRES_IU - DET_TCOMP_STD_PRES_IU);

         gas_table->Frit.Setpt_Pres_Correct_Per_P_IU[entry_num] =
                  (cor_pres - std_pres) * CORRECT_SCALE / (DET_TCOMP_COR_PRES_IU - DET_TCOMP_STD_PRES_IU);

     }

}


void BuildDetGasCompTable ( pDETECTOR_STATUS status, U_INT16 gas_num )
{
    pDET_GAS_TABLE    gas_table;
    GAS_TYPE      gas_type;

    if ( !EpcDetector ( status ) ) return;


    switch( gas_num )
    {
        case 1: status->Gas_1.Comp_Tables_Ok = FALSE;
                gas_table = status->Gas_1.Tcomp_Tables;
                gas_type  = status->Config->Gas_1.Gas_Type;
                break;
        case 2: status->Gas_2.Comp_Tables_Ok = FALSE;
                gas_table = status->Gas_2.Tcomp_Tables;
                gas_type  = status->Config->Gas_2.Gas_Type;
                break;
        default:
        case 3: status->Gas_3.Comp_Tables_Ok = FALSE;
                gas_table = status->Gas_3.Tcomp_Tables;
                gas_type  = status->Config->Gas_3.Gas_Type;
                break;
    }

    GetPresSensorConstants ( status->Module_Number, gas_num, &gas_table->Pres_Sensor.Parms );

    GetFritConstants       ( status, gas_num, &gas_table->Frit.Parms );
    GetConfigConstants     ( status, gas_table );

    if (gas_table->Frit.Parms.Max_Flow != 0 )
    {
         BuildFlowToPresTable ( gas_table, gas_type );
         BuildPresToFlowTable ( gas_table, gas_type );

         BuildInitialCorrectionTable ( gas_table, gas_type );
    }


    switch( gas_num )
    {
        case 1: status->Gas_1.Comp_Tables_Ok = TRUE;
                break;
        case 2: status->Gas_2.Comp_Tables_Ok = TRUE;
                break;
        case 3: status->Gas_3.Comp_Tables_Ok = TRUE;
                break;
    }


}


#ifdef DET_CALIB_TEST



void KLUDGE_LoadFritParms ( pDETECTOR_STATUS status, U_INT16 gas_num, U_INT32 alpha, U_INT32 beta )
{
    switch( gas_num )
    {
        case 1: status->Gas_1.Comp_Tables_Ok = FALSE;

                status->Gas_1.Tcomp_Tables->Frit.Parms.frit_1 = alpha;
                status->Gas_1.Tcomp_Tables->Frit.Parms.frit_2 = beta;

                BuildFlowToPresTable ( status->Gas_1.Tcomp_Tables, status->Config->Gas_1.Gas_Type );
                BuildPresToFlowTable (  status->Gas_1.Tcomp_Tables, status->Config->Gas_1.Gas_Type );
                BuildInitialCorrectionTable (  status->Gas_1.Tcomp_Tables, status->Config->Gas_1.Gas_Type );

                status->Gas_1.Comp_Tables_Ok = TRUE;
                break;

        case 2: status->Gas_2.Comp_Tables_Ok = FALSE;

                status->Gas_2.Tcomp_Tables->Frit.Parms.frit_1 = alpha;
                status->Gas_2.Tcomp_Tables->Frit.Parms.frit_2 = beta;

                BuildFlowToPresTable ( status->Gas_2.Tcomp_Tables, status->Config->Gas_2.Gas_Type );
                BuildPresToFlowTable (  status->Gas_2.Tcomp_Tables, status->Config->Gas_2.Gas_Type );
                BuildInitialCorrectionTable (  status->Gas_2.Tcomp_Tables, status->Config->Gas_2.Gas_Type );

                status->Gas_2.Comp_Tables_Ok = TRUE;
                break;

        case 3: status->Gas_3.Comp_Tables_Ok = FALSE;

                status->Gas_3.Tcomp_Tables->Frit.Parms.frit_1 = alpha;
                status->Gas_3.Tcomp_Tables->Frit.Parms.frit_2 = beta;

                BuildFlowToPresTable ( status->Gas_3.Tcomp_Tables, status->Config->Gas_3.Gas_Type );
                BuildPresToFlowTable (  status->Gas_3.Tcomp_Tables, status->Config->Gas_3.Gas_Type );
                BuildInitialCorrectionTable (  status->Gas_3.Tcomp_Tables, status->Config->Gas_3.Gas_Type );

                status->Gas_3.Comp_Tables_Ok = TRUE;

                break;
    }
}


void KLUDGE_LoadPsParms ( pDETECTOR_STATUS status, U_INT16 gas_num, U_INT16 offset, U_INT16 span,
                                                                    INT16   do_dt,  INT16   ds_dt  )
{
    switch( gas_num )
    {
        case 1: status->Gas_1.Comp_Tables_Ok = FALSE;
                status->Gas_1.Tcomp_Tables->Pres_Sensor.Parms.Offset     = offset;
                status->Gas_1.Tcomp_Tables->Pres_Sensor.Parms.dOffset_dT = do_dt;
                status->Gas_1.Tcomp_Tables->Pres_Sensor.Parms.Span       = span;
                status->Gas_1.Tcomp_Tables->Pres_Sensor.Parms.dSpan_dT   = ds_dt;
                break;
        case 2: status->Gas_2.Comp_Tables_Ok = FALSE;
                status->Gas_2.Tcomp_Tables->Pres_Sensor.Parms.Offset     = offset;
                status->Gas_2.Tcomp_Tables->Pres_Sensor.Parms.dOffset_dT = do_dt;
                status->Gas_2.Tcomp_Tables->Pres_Sensor.Parms.Span       = span;
                status->Gas_2.Tcomp_Tables->Pres_Sensor.Parms.dSpan_dT   = ds_dt;
                break;
        case 3: status->Gas_3.Comp_Tables_Ok = FALSE;
                status->Gas_3.Tcomp_Tables->Pres_Sensor.Parms.Offset     = offset;
                status->Gas_3.Tcomp_Tables->Pres_Sensor.Parms.dOffset_dT = do_dt;
                status->Gas_3.Tcomp_Tables->Pres_Sensor.Parms.Span       = span;
                status->Gas_3.Tcomp_Tables->Pres_Sensor.Parms.dSpan_dT   = ds_dt;
                break;
    }
}


void KLUDGE_LoadThermParms ( pDETECTOR_STATUS status, U_INT16 v25c, INT16 delta_v, INT16 delta_t  )
{

     U_INT16 i;

     status->Tcomp_Tables->Thermistor.Parms.V_25C = v25c;
     status->Tcomp_Tables->Thermistor.Parms.dV    = delta_v;
     status->Tcomp_Tables->Thermistor.Parms.dT    = delta_t;


     for ( i = 0 ; i < 256 ; i++ )
     {
         *( (U_INT16 *)(status->Tcomp_Tables->Thermistor.Volts_To_Temp) + i) =
                    FindTrueTemp( &status->Tcomp_Tables->Thermistor.Parms, i*256 );
     }
         *( (U_INT16 * )(status->Tcomp_Tables->Thermistor.Volts_To_Temp) + i) =
                    FindTrueTemp( &status->Tcomp_Tables->Thermistor.Parms, 0xffff );

}


#endif

/**begin_proto**/

void ReBuildDetGasCompTable ( pDETECTOR_STATUS status, U_INT16 gas_num )

/**end_proto**/
{
    pDET_GAS_TABLE    gas_table;
    GAS_TYPE      gas_type;

    if ( !EpcDetector ( status ) ) return;

    switch( gas_num )
    {
        case 1: status->Gas_1.Comp_Tables_Ok = FALSE;
                gas_table = status->Gas_1.Tcomp_Tables;
                gas_type  = status->Config->Gas_1.Gas_Type;
                break;
        case 2: status->Gas_2.Comp_Tables_Ok = FALSE;
                gas_table = status->Gas_2.Tcomp_Tables;
                gas_type  = status->Config->Gas_2.Gas_Type;
                break;
        default:
        case 3: status->Gas_3.Comp_Tables_Ok = FALSE;
                gas_table = status->Gas_3.Tcomp_Tables;
                gas_type  = status->Config->Gas_3.Gas_Type;
                break;
    }


    GetFritConstants       ( status, gas_num, &gas_table->Frit.Parms );
    GetConfigConstants     ( status, gas_table );

    if (gas_table->Frit.Parms.Max_Flow != 0 )
    {
         BuildFlowToPresTable ( gas_table, gas_type );
         BuildPresToFlowTable ( gas_table, gas_type );
         BuildInitialCorrectionTable ( gas_table, gas_type );
    }

    DownloadDetPids( status );

    switch( gas_num )
    {
        case 1: status->Gas_1.Comp_Tables_Ok = TRUE;
                break;
        case 2: status->Gas_2.Comp_Tables_Ok = TRUE;
                break;
        case 3: status->Gas_3.Comp_Tables_Ok = TRUE;
                break;
    }

}

/**begin_proto**/

void DoDetCompTables( pDETECTOR_STATUS status )

/**end_proto**/
{

     BuildDetGasCompTable ( status, 1 );
     BuildDetGasCompTable ( status, 2 );
     BuildDetGasCompTable ( status, 3 );

}

/***********************************************************************/
/* FUNCTION:                                                           */
/* ========                                                            */
/*                                                                     */
/* PURPOSE:                                                            */
/*                                                                     */
/* RETURNED VALUE:                                                     */
/*                                                                     */
/* GLOBALS USED:                                                       */
/*                                                                     */
/***********************************************************************/
/**begin_proto**/

BIT8 ReadFlowSensorConstants ( pINLET_STATUS status, GAS_TYPE gas,  pFLOW_SENSOR_PARMS parms )

/**end_proto**/
{
     U_INT16 module_number;

     U_INT16 offset, alpha_high,            beta_high,           high_flow, reference_temp, max_flow;
     INT16   dAlpha_dT, dBeta_dT, dOffset_dT;
     INT16   alpha_low, beta_low;
     BIT8    result;

     result = TRUE;

     module_number = status->Module_Number;

     result &= ReadEeprom( module_number,
                           GetFSensorParmAddress( status->Config->Type, gas, OFFSET         ),
                           &offset );

     result &= ReadEeprom( module_number,
                           GetFSensorParmAddress( status->Config->Type, gas, ALPHA_LOW      ),
                           (U_INT16 *)&alpha_low );

     result &= ReadEeprom( module_number,
                           GetFSensorParmAddress( status->Config->Type, gas, ALPHA_HIGH     ),
                           &alpha_high );

     result &= ReadEeprom( module_number,
                           GetFSensorParmAddress( status->Config->Type, gas, BETA_LOW       ),
                           (U_INT16 *)&beta_low );

     result &= ReadEeprom( module_number,
                           GetFSensorParmAddress( status->Config->Type, gas, BETA_HIGH      ),
                           &beta_high );

     result &= ReadEeprom( module_number,
                           GetFSensorParmAddress( status->Config->Type, gas, SWITCH_FLOW    ),
                           &high_flow );

     result &= ReadEeprom( module_number,
                           GetFSensorParmAddress( status->Config->Type, gas, DALPHA         ),
                           (U_INT16 *)&dAlpha_dT );

     result &= ReadEeprom( module_number,
                           GetFSensorParmAddress( status->Config->Type, gas, DBETA          ),
                           (U_INT16 *)&dBeta_dT );

     result &= ReadEeprom( module_number,
                           GetFSensorParmAddress( status->Config->Type, gas, DOFFSET        ),
                           (U_INT16 *)&dOffset_dT );

     result &= ReadEeprom( module_number,
                           GetFSensorParmAddress( status->Config->Type, gas, REF_TEMP       ),
                           (U_INT16 *)&reference_temp );

     result &= ReadEeprom( module_number,
                           GetFSensorParmAddress( status->Config->Type, gas, MAX_INLET_FLOW ),
                           (U_INT16 *)&max_flow );

   /* delta_offset_delta_t is a scaled integer version of dOffset_dT thaat can be used in the foreground */
   /* routines.  dOffset_dT is a floating point version that can be used to build tables during initialization */

#if 0
     parms->Offset     =     (offset & 0xff00);
     parms->Offset_Offset =  (offset & 0x00ff);
#else
     parms->Offset     =     offset;
#endif
     parms->delta_offset_delta_t = (INT16) parms->dOffset_dT;

     parms->Alpha_High = (float) alpha_high * FS_ALPHA_SCALING;
     parms->Alpha_Low  = (float) alpha_low  * FS_ALPHA_SCALING;
     parms->Beta_High  = (float) beta_high  / FS_BETA_SCALING;
     parms->Beta_Low   = (float) beta_low   / FS_BETA_SCALING;
     parms->dAlpha_dT  = (float) dAlpha_dT  / FS_DALPHA_SCALING;
     parms->dBeta_dT   = (float) dBeta_dT   / FS_DBETA_SCALING;
     parms->dOffset_dT = (float) dOffset_dT / FS_DOFFSET_SCALING;
     parms->High_Flow  = (float) high_flow;
     parms->Reference_Temp = reference_temp;

     parms->Max_Flow   = (U_INT32)max_flow * status->IU_Scaling;

     if ( high_flow == 0xffff ) /* no high flow specified */
     {
         parms->High_Volts = 0xffff;
     }
     else
     {
         parms->High_Volts = CalcFlowToVolts( parms, parms->High_Flow );
     }

     return result;
}

/**begin_proto**/
U_INT16 CalcFlowToVolts ( pFLOW_SENSOR_PARMS parms, float flow )
/**end_proto**/
{

     if ( flow  >= parms->High_Flow )
     {
         return (U_INT16)(  parms->Alpha_High * ( 1.0 - exp( -parms->Beta_High * flow ) ) );
     }
     else
     {
         return (U_INT16)(  parms->Alpha_Low  * ( 1.0 - exp( -parms->Beta_Low  * flow ) ) );
     }

}


U_INT32 CalcVoltsToFlow ( pFLOW_SENSOR_PARMS parms, U_INT16 volts, U_INT32 IU_Scaling )
{

     float log_term;
     float alpha, beta;
     float flow;

     if ( volts >= parms->High_Volts )
     {
          alpha  = parms->Alpha_High;
          beta   = parms->Beta_High;
     }
     else
     {
          alpha = parms->Alpha_Low;
          beta  = parms->Beta_Low;
     }

      /* revisit: clamp if voltage exceeds sensor theoretic maximum voltage  */
      /* optionaly, determine slope at limit, and use a linear approximation */
      /* the -1 clamps one adc count below the threshold.                    */

     if  ( volts > fabs(alpha)  ) volts = (U_INT16)( fabs(alpha) - 1 );

     log_term = 1 - (float)volts / alpha;

     if ( log_term > 0 )
     {
          flow = - log ( log_term ) / beta;
          if ( flow < 0.0 ) flow = 0.0;
          flow = flow * (float)IU_Scaling;
          return (U_INT32)flow;
     }
     else
     {
          return 0;
     }
}


float CalcCorrection ( pFLOW_SENSOR_PARMS parms, U_INT32 volts)
{

     float foo;
     float alpha, beta;

     if ( volts >= parms->High_Volts )
     {
          alpha  = parms->Alpha_High;
          beta   = parms->Beta_High;
     }
     else
     {
          alpha = parms->Alpha_Low;
          beta  = parms->Beta_Low;
     }

     foo = ( volts - parms->Offset ) / alpha;

     /* the 64.0 is just scaling to avoid loss of precision in integer math */

     return  64.0 * (  foo * parms->dAlpha_dT
                       - ( alpha / beta ) * log ( 1- foo ) * ( 1 - foo ) * parms->dBeta_dT );
}


void BuildFsFlowToVolts( pFLOW_SENSOR_STRUCT sensor, U_INT32 IU_Scaling )
{


     U_INT16 i;

     for ( i = 0 ; i< sensor->Num_Entries ; i++ )
     {
         *( sensor->Flow_To_Volts +i ) =
                 CalcFlowToVolts ( &sensor->Parms, (float)i * sensor->Entry_Spacing / IU_Scaling );
     }

     *( sensor->Flow_To_Volts +i ) =
                 CalcFlowToVolts ( &sensor->Parms, (float)i * sensor->Entry_Spacing / IU_Scaling );

}


void BuildFsVoltsToFlow( pFLOW_SENSOR_STRUCT sensor, U_INT32 IU_Scaling)
{

     U_INT16 i;


     for ( i = 0 ; i< REF_TABLE_SIZE ; i++ )
     {
         *( sensor->Volts_To_Flow +i ) = CalcVoltsToFlow ( &sensor->Parms, i*256, IU_Scaling );
     }

     *( sensor->Volts_To_Flow +i ) = CalcVoltsToFlow ( &sensor->Parms, 0xffff, IU_Scaling );

}


void BuildFsCorrectTable ( pFLOW_SENSOR_STRUCT sensor)
{
      U_INT16 i;

      for ( i = 0 ; i < INLET_CORRECT_TABLE_SIZE ; i++ )
      {
          *( sensor->dV_dT + i ) = (U_INT16)CalcCorrection ( &sensor->Parms, (i<<11)  );
      }

      *( sensor->dV_dT + i ) = (U_INT16)CalcCorrection ( &sensor->Parms, 0xffff );


}

/**begin_proto**/

void InitFlowTempCompTable( pINLET_STATUS status )

/**end_proto**/
{

     U_INT32 max_flow;

     if ( !ReadFlowSensorConstants( status, status->Config->Gas_Type,  &status->Tcomp_Tables->Flow_Sensor.Parms ) )
     {
          TrashModule( status->Module_Number );
          return;
     }


     max_flow = GetInletMaxFlowIu ( status, status->IU_Scaling ) * 2; /* leave room for atmp and user calib growth */

     FindFlowMasks ( max_flow,
                     REF_TABLE_SIZE,
                     &status->Tcomp_Tables->Flow_Sensor.Mask,
                     &status->Tcomp_Tables->Flow_Sensor.Entry_Spacing,
                     &status->Tcomp_Tables->Flow_Sensor.Shift_Value,
                     status->IU_Scaling  );


     /* the table resolution only needs to be one entry per mL/min, so if  */
     /* max flow < Max Table Size, make the table max flow entries long    */

     status->Tcomp_Tables->Flow_Sensor.Num_Entries =
                   ( (max_flow/status->Tcomp_Tables->Flow_Sensor.Entry_Spacing) < REF_TABLE_SIZE )
                   ? (max_flow/status->Tcomp_Tables->Flow_Sensor.Entry_Spacing) +1
                   :  REF_TABLE_SIZE;


                     status->Tcomp_Tables->Flow_Sensor.Comp_Mask = 0xffff;
                     status->Tcomp_Tables->Flow_Sensor.Comp_Entry_Spacing = 0;
                     status->Tcomp_Tables->Flow_Sensor.Comp_Shift = 3;

     BuildFsFlowToVolts ( &status->Tcomp_Tables->Flow_Sensor,
                          status->IU_Scaling );

     BuildFsVoltsToFlow ( &status->Tcomp_Tables->Flow_Sensor,
                          status->IU_Scaling );

     BuildFsCorrectTable (  &status->Tcomp_Tables->Flow_Sensor );

}

/**begin_proto**/

void InitPresTempComp( pINLET_STATUS status )

/**end_proto**/
{
     /* the pressure sensor for the inlet is the gas #1 detector position */

     GetPresSensorConstants( status->Module_Number, 1,&status->Tcomp_Tables->Pres_Sensor.Parms );

}


/***********************************************************************/
/* FUNCTION:                                                           */
/* ========                                                            */
/*                                                                     */
/* PURPOSE:                                                            */
/*                                                                     */
/* RETURNED VALUE:                                                     */
/*                                                                     */
/* GLOBALS USED:                                                       */
/*                                                                     */
/***********************************************************************/
/**begin_proto**/

void InitPurgeRegComp ( pINLET_STATUS status )

/**end_proto**/
{


     U_INT16 purge_flow;
     INT16   dflow_dpres;
     U_INT32 scaling;
     GAS_TYPE gas_type;
     BIT8    result;

     result = TRUE;

     for ( gas_type = N2 ;  gas_type <= ArMe ; gas_type++ )
     {

          result &= ReadEeprom( status->Module_Number,
                      GetFSensorParmAddress( status->Config->Type, gas_type, PURGE_REG_FLOW ),
                      &purge_flow );

          result &= ReadEeprom( status->Module_Number,
                      GetFSensorParmAddress( status->Config->Type, gas_type, DPURGE_FLOW_DPRES ),
                      (U_INT16 *)&dflow_dpres       );


          ReadPurgeFlowScaling( status->Module_Number, &scaling );


            /*   iu  <- scaled mL/Min */
          status->Tcomp_Tables->Purge_Regulator.Parms[ (U_INT8) gas_type ].Purge_Flow =
                    QuadWordMultiplyDivide( purge_flow, status->IU_Scaling, scaling );

            /* scaled iu / p_iu  */
          status->Tcomp_Tables->Purge_Regulator.Parms[ (U_INT8) gas_type ].dFlow_dPres =
                           SignedQuadWordMultiplyDivide( dflow_dpres, status->IU_Scaling, scaling );

     }

     if ( !result )
     {
         TrashModule( status->Module_Number );
     }
}


/***********************************************************************/
/* FUNCTION:                                                           */
/* ========                                                            */
/*                                                                     */
/* PURPOSE:                                                            */
/*                                                                     */
/* RETURNED VALUE:                                                     */
/*                                                                     */
/* GLOBALS USED:                                                       */
/*                                                                     */
/***********************************************************************/
/**begin_proto**/

UI_ERR ReadPurgeRegComp ( pINLET_STATUS status, GAS_TYPE gas, U_INT32 *flow, INT32 *df_dp )

/**end_proto**/
{

     *flow  = status->Tcomp_Tables->Purge_Regulator.Parms[ (U_INT8) gas ].Purge_Flow;
     *df_dp = status->Tcomp_Tables->Purge_Regulator.Parms[ (U_INT8) gas ].dFlow_dPres;

     return OK;
}

/**begin_proto**/

void RebuildInletCompTables( pINLET_STATUS status )

/**end_proto**/
{
     InitPurgeRegComp ( status );
     InitFlowTempCompTable( status );
     DownloadInletPids ( status );
}


/**begin_proto**/

void DoAuxCompTables( pAUXILIARY_STATUS status )

/**end_proto**/
{

   GetPresSensorConstants( status->Module_Number, 1, &status->Tcomp_Tables->Aux_3.Parms );
   GetPresSensorConstants( status->Module_Number, 2, &status->Tcomp_Tables->Aux_4.Parms );
   GetPresSensorConstants( status->Module_Number, 3, &status->Tcomp_Tables->Aux_5.Parms );

}

/**begin_proto**/

void GetGas_2MinFlowRate ( pDETECTOR_STATUS status, U_INT8 module_number )

/**end_proto**/
{
   U_INT16 min_flow;

   if ( !ReadEeprom( module_number, GAS_2_MIN_FLOW, &min_flow ) )
   {
        TrashModule( status->Module_Number );
   }

   if ( ( status->Config->Type == EPC_TCD ) && (min_flow == 0 ) )
   {
     min_flow  =  MIN_TCD_REF_FLOW ;
   }

   if ( ( status->Config->Type == EPC_LDID ) && (min_flow == 0 ) )
   {
     min_flow  =  MIN_LDID_GAS_FLOW ;
   }

   status->Min_Gas_2_Flow_Rate =  (U_INT32) min_flow * status->Gas_2.IU_Scaling;

}

/**begin_proto**/

#pragma SECTION UNDO

/**end_proto**/
